/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.apache.jena.rdf.model.test;

import java.util.ArrayList;
import java.util.List;

import org.apache.jena.rdf.model.*;
import org.apache.jena.rdf.model.test.helpers.ModelHelper;
import org.apache.jena.rdf.model.test.helpers.TestingModelFactory;
import org.apache.jena.test.JenaTestBase;
import org.junit.Assert;

/**
 * This class tests various properties of RDFNodes.
 */
public class TestRDFNodes extends AbstractModelTestBase {

    public TestRDFNodes(final TestingModelFactory modelFactory, final String name) {
        super(modelFactory, name);
    }

    public void testInModel() {
        final Model m1 = ModelHelper.modelWithStatements(this, "");
        final Model m2 = ModelHelper.modelWithStatements(this, "");
        final Resource r1 = ModelHelper.resource(m1, "r1");
        final Resource r2 = ModelHelper.resource(m1, "_r2");
        /* */
        Assert.assertEquals(r1.getModel(), m1);
        Assert.assertEquals(r2.getModel(), m1);
        Assert.assertFalse(r1.isAnon());
        Assert.assertTrue(r2.isAnon());
        /* */
        Assert.assertEquals(r1.inModel(m2).getModel(), m2);
        Assert.assertEquals(r2.inModel(m2).getModel(), m2);
        /* */
        Assert.assertEquals(r1, r1.inModel(m2));
        Assert.assertEquals(r2, r2.inModel(m2));
    }

    public void testIsAnon() {
        final Model m = ModelHelper.modelWithStatements(this, "");
        Assert.assertEquals(false, m.createResource("eh:/foo").isAnon());
        Assert.assertEquals(true, m.createResource().isAnon());
        Assert.assertEquals(false, m.createTypedLiteral(17).isAnon());
        Assert.assertEquals(false, m.createTypedLiteral("hello").isAnon());
    }

    public void testIsLiteral() {
        final Model m = ModelHelper.modelWithStatements(this, "");
        Assert.assertEquals(false, m.createResource("eh:/foo").isLiteral());
        Assert.assertEquals(false, m.createResource().isLiteral());
        Assert.assertEquals(true, m.createTypedLiteral(17).isLiteral());
        Assert.assertEquals(true, m.createTypedLiteral("hello").isLiteral());
    }

    public void testIsResource() {
        final Model m = ModelHelper.modelWithStatements(this, "");
        Statement stmt = ModelHelper.statement("S P O");
        StatementTerm tripleTerm = m.createStatementTerm(stmt);
        Assert.assertEquals(true, m.createResource("eh:/foo").isResource());
        Assert.assertEquals(true, m.createResource().isResource());
        Assert.assertEquals(false, m.createTypedLiteral(17).isResource());
        Assert.assertEquals(false, m.createTypedLiteral("hello").isResource());
        Assert.assertEquals(false, tripleTerm.isResource());
    }

    public void testIsURIResource() {
        final Model m = ModelHelper.modelWithStatements(this, "");
        Assert.assertEquals(true, m.createResource("eh:/foo").isURIResource());
        Assert.assertEquals(false, m.createResource().isURIResource());
        Assert.assertEquals(false, m.createTypedLiteral(17).isURIResource());
        Assert.assertEquals(false, m.createTypedLiteral("hello").isURIResource());
    }

    public void testIsStatementTerm1() {
        final Model m = ModelHelper.modelWithStatements(this, "");
        Statement stmt = ModelHelper.statement("S P O");
        StatementTerm tripleTerm = m.createStatementTerm(stmt);
        Assert.assertEquals(false, m.createResource("eh:/foo").isStatementTerm());
        Assert.assertEquals(false, m.createResource().isStatementTerm());
        Assert.assertEquals(false, m.createTypedLiteral(17).isStatementTerm());
        Assert.assertEquals(false, m.createTypedLiteral("hello").isStatementTerm());
        Assert.assertEquals(true, tripleTerm.isStatementTerm());
    }

    public void testIsStatementTerm2() {
        final Model m = ModelHelper.modelWithStatements(this, "");
        Statement stmt = ModelHelper.statement("S P O");
        StatementTerm tripleTerm = m.createStatementTerm(stmt);
        Assert.assertEquals(false, tripleTerm.isAnon());
        Assert.assertEquals(false, tripleTerm.isURIResource());
        Assert.assertEquals(false, tripleTerm.isLiteral());
        Assert.assertEquals(false, tripleTerm.isResource());
        Assert.assertEquals(true, tripleTerm.isStatementTerm());
    }

    public void testLiteralAsResourceThrows() {
        final Model m = ModelHelper.modelWithStatements(this, "");
        final Resource r = m.createResource("eh:/spoo");
        try {
            r.asLiteral();
            Assert.fail("should not be able to do Resource.asLiteral()");
        } catch (final LiteralRequiredException e) {}
    }

    public void testRDFNodeAsLiteral() {
        final Model m = ModelHelper.modelWithStatements(this, "");
        final Literal l = m.createLiteral("hello, world");
        Assert.assertSame(l, l.asLiteral());
    }

    public void testRDFNodeAsResource() {
        final Model m = ModelHelper.modelWithStatements(this, "");
        final Resource r = m.createResource("eh:/spoo");
        Assert.assertSame(r, r.asResource());
    }

    public void testRDFVisitor() {
        final List<String> history = new ArrayList<>();
        final Model m = ModelFactory.createDefaultModel();
        final RDFNode S = m.createResource();
        final RDFNode P = m.createProperty("eh:PP");
        final RDFNode O = m.createLiteral("LL");
        final Statement stmt = m.createStatement((Resource)S, (Property)P, O);
        final RDFNode ST = m.createStatementTerm(stmt);

        /* */
        final RDFVisitor rv = new RDFVisitor() {
            @Override
            public Object visitBlank(final Resource R, final AnonId id) {
                history.add("blank");
                Assert.assertTrue("must visit correct node", R == S);
                Assert.assertEquals("must have correct field", R.getId(), id);
                return "blank result";
            }

            @Override
            public Object visitLiteral(final Literal L) {
                history.add("literal");
                Assert.assertTrue("must visit correct node", L == O);
                return "literal result";
            }

            @Override
            public Object visitURI(final Resource R, final String uri) {
                history.add("uri");
                Assert.assertTrue("must visit correct node", R == P);
                Assert.assertEquals("must have correct field", R.getURI(), uri);
                return "uri result";
            }

            @Override
            public Object visitStmt(StatementTerm statementTerm, Statement statement) {
                history.add("statementTerm");
                return "statement term result";
            }
        };
        /* */
        Assert.assertEquals("blank result", S.visitWith(rv));
        Assert.assertEquals("uri result", P.visitWith(rv));
        Assert.assertEquals("literal result", O.visitWith(rv));
        Assert.assertEquals("statement term result", ST.visitWith(rv));

        Assert.assertEquals(JenaTestBase.listOfStrings("blank uri literal statementTerm"), history);
    }

    public void testRemoveAllBoring() {
        final Model m1 = ModelHelper.modelWithStatements(this, "x P a; y Q b");
        final Model m2 = ModelHelper.modelWithStatements(this, "x P a; y Q b");
        ModelHelper.resource(m2, "x").removeAll(ModelHelper.property(m2, "Z"));
        ModelHelper.assertIsoModels("m2 should be unchanged", m1, m2);
    }

    public void testRemoveAllRemoves() {
        final String ps = "x P a; x P b", rest = "x Q c; y P a; y Q b";
        final Model m = ModelHelper.modelWithStatements(this, ps + "; " + rest);
        final Resource r = ModelHelper.resource(m, "x");
        final Resource r2 = r.removeAll(ModelHelper.property(m, "P"));
        Assert.assertSame("removeAll should deliver its receiver", r, r2);
        ModelHelper.assertIsoModels("x's P-values should go", ModelHelper.modelWithStatements(this, rest), m);
    }

    public void testResourceAsLiteralThrows() {
        final Model m = ModelHelper.modelWithStatements(this, "");
        final Literal l = m.createLiteral("hello, world");
        try {
            l.asResource();
            Assert.fail("should not be able to do Literal.asResource()");
        } catch (final ResourceRequiredException e) {}
    }
}
